package com.wang.rest

import com.alibaba.druid.pool.DruidDataSource
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.alibaba.fastjson.util.IOUtils
import com.wang.rest.exception.RestException
import com.wang.template.entity.Table
import com.wang.utils.DatabaseUtils
import java.io.FileReader
import java.sql.Connection
import java.util.*
import java.util.function.BiFunction
import java.util.function.Function
import javax.sql.DataSource
import java.sql.SQLException
import org.apache.tomcat.jni.SSL.setPassword
import java.io.PrintWriter
import java.util.logging.Logger


fun main() {
    val map = mapOf(
        "host" to "localhost",
        "port" to "3306",
        "database" to "wdj",
        "user" to "root",
        "password" to "root"
    )
    val dataSource = DruidDataSource()
    dataSource.url = "jdbc:mysql://${map["host"]}:${map["port"]}/${map["database"]}?useSSL=false&serverTimezone=UTC"
    dataSource.username = map["user"]
    dataSource.password = map["password"]
    dataSource.driverClassName = "com.mysql.jdbc.Driver"
    val server = RestServer(map, dataSource)
    val req = RestServer.Request("/user/id/1", "put", "{\"name\": \"wdwwwdj\"}")
    val sql = server.execute(req)
    println(sql)

}

/**
 * 根据http请求生成对应的sql语句
 */
class RestServer(dataInfo: Map<String, String>, private val dataSource: DataSource) {
    private val tableMap = DatabaseUtils.loadDatabase(dataInfo).map { it.name to it }.toMap()

    fun execute(req: Request): Any {
        val items = req.url.split("/").dropWhile { it == "" }
        if (items.size % 2 != 1) {
            throw RestException.INVALID_URL
        }
        val table = items[0]
        val colMap = tableMap.getValue(table).columnList
        val fixValue = { key: String, value: Any -> if (colMap[key]!!.javaType == "String") "\"$value\"" else value }

        val kv = items.drop(0).foldRightIndexed(mutableMapOf<String, Any>()) { index, s, acc ->
            if (index % 2 != 0) {
                acc[s] = fixValue(s, items[index + 1])
            }
            acc
        }
        val json = if (req.body == null) null else JSONObject.parseObject(req.body).apply {
            this.entries.forEach { kv ->
                this[kv.key] = fixValue(kv.key, kv.value)
            }
        }

        val sql = when (req.method) {
            "get" -> selectFun(table, kv)
            "put" -> updateFun(table, kv, json!!)
            "post" -> insertFun(table, json!!)
            "delete" -> deleteFun(table, kv)
            else -> throw Exception()
        }
        println(sql)
        val conn = this.dataSource.connection
        return when (req.method) {
            "get" -> DatabaseUtils.select(conn, sql)
            "put" -> DatabaseUtils.update(conn, sql)
            "post" -> DatabaseUtils.insert(conn, sql)
            "delete" -> DatabaseUtils.delete(conn, sql)
            else -> ""
        }
    }

    private fun selectFun(table: String, kv: Map<String, Any>): String {
        val s = if (kv.entries.isEmpty()) "" else kv.entries.joinToString(
            separator = " and ",
            prefix = "where "
        ) { "${it.key} = ${it.value}" }
        return "select * from $table $s"
    }

    private fun insertFun(table: String, json: Map<String, Any>): String {
        val fields = json.map { it.key }.joinToString(separator = ",", prefix = "(", postfix = ")")
        val values = json.map { it.value }.joinToString(separator = ",", prefix = "(", postfix = ")")
        return "insert into $table $fields values $values"
    }

    private fun updateFun(table: String, kv: Map<String, Any>, json: Map<String, Any>): String {
        val updateSql = kv.entries.joinToString(separator = " and ") { "${it.key} = ${it.value}" }
        val valueSql = json.entries.joinToString(separator = " and ") { "${it.key} = ${it.value}" }
        return "update $table set $valueSql where $updateSql"
    }

    private fun deleteFun(table: String, kv: Map<String, Any>): String {
        val s = kv.entries.joinToString(separator = " and ", prefix = "where ") { "${it.key} = ${it.value}" }
        return "delete from $table $s"
    }

    data class Request(val url: String, val method: String, val body: String?)
}
